use crate::entity::Entity;
use crate::TableDef;
use quote::quote;
use proc_macro::{TokenStream};

#[derive(Clone, Debug)]
pub struct EntityPrinter {
    pub(crate) entities: Vec<TableDef>,
}

pub struct PrinterOutput {
    pub files: Vec<OutputFile>,
}

pub struct OutputFile {
    pub name: String,
    pub content: String,
}

impl EntityPrinter {
    pub fn generate(self, ) {

    }

    pub fn write_entities(&self, expanded_format: bool) -> Vec<OutputFile> {
        self.entities
            .iter()
            .map(|entity| {
                let mut lines = Vec::new();
                Self::write_doc_comment(&mut lines);
                let code_blocks = if expanded_format {
                    Self::gen_expanded_code_blocks(entity)
                } else {
                    Self::gen_compact_code_blocks(entity)
                };
                Self::write(&mut lines, code_blocks);
                OutputFile {
                    name: format!("{}.rs", entity.get_table_name_snake_case()),
                    content: lines.join("\n\n"),
                }
            })
            .collect()
    }

    pub fn write_doc_comment(lines: &mut Vec<String>) {
        let ver = env!("CARGO_PKG_VERSION");
        let comments = vec![format!(
            "//! Akita Entity. Generated by akita_codegen {}",
            ver
        )];
        lines.extend(comments);
        lines.push("".to_owned());
    }

    pub fn gen_compact_code_blocks(entity: &Entity) -> Vec<TokenStream> {
        let mut imports = Self::gen_import();
        imports.extend(Self::gen_import_active_enum(entity));
        let mut code_blocks = vec![imports, Self::gen_compact_model_struct(entity)];
        code_blocks.extend(Self::gen_impl_conjunct_related(entity));
        code_blocks.extend(vec![Self::gen_impl_active_model_behavior()]);
        code_blocks
    }

    pub fn gen_compact_model_struct(entity: &Entity) -> TokenStream {
        let table_name = entity.table_name().as_str();
        let column_names_snake_case = entity.get_column_names_snake_case();
        let column_rs_types = entity.get_column_rs_types();
        let primary_keys: Vec<String> = entity
            .primary_keys
            .iter()
            .map(|pk| pk.name.clone())
            .collect();
        let attrs: Vec<TokenStream> = entity
            .columns
            .iter()
            .map(|col| {
                let mut attrs: Punctuated<_, Comma> = Punctuated::new();
                if primary_keys.contains(&col.name) {
                    attrs.push(quote! { primary_key });
                    if !col.auto_increment {
                        attrs.push(quote! { auto_increment = false });
                    }
                }
                if let Some(ts) = col.get_col_type_attrs() {
                    attrs.extend(vec![ts]);
                    if !col.not_null {
                        attrs.push(quote! { nullable });
                    }
                };
                if col.unique {
                    attrs.push(quote! { unique });
                }
                if !attrs.is_empty() {
                    let mut ts = TokenStream::new();
                    for (i, attr) in attrs.into_iter().enumerate() {
                        if i > 0 {
                            ts = quote! { #ts, };
                        }
                        ts = quote! { #ts #attr };
                    }
                    quote! {
                        #[sea_orm(#ts)]
                    }
                } else {
                    TokenStream::new()
                }
            })
            .collect();

        let extra_derive = with_serde.extra_derive();

        quote! {
            #[derive(Clone, Debug, PartialEq, DeriveEntityModel #extra_derive)]
            #[sea_orm(table_name = #table_name)]
            pub struct Model {
                #(
                    #attrs
                    pub #column_names_snake_case: #column_rs_types,
                )*
            }
        }
    }

    pub fn gen_import() -> TokenStream {
        let prelude_import = quote!(
            use akiata::*;
        );
        prelude_import
    }

    pub fn gen_expanded_code_blocks(entity: &Entity) -> Vec<TokenStream> {
        let mut imports = Self::gen_import();
        let mut code_blocks = vec![
            imports,
            Self::gen_entity_struct(),
            // Self::gen_impl_entity_name(entity),
            // Self::gen_model_struct(entity, with_serde),
            // Self::gen_column_enum(entity),
            // Self::gen_primary_key_enum(entity),
            // Self::gen_impl_primary_key(entity),
            // Self::gen_relation_enum(entity),
            // Self::gen_impl_column_trait(entity),
            // Self::gen_impl_relation_trait(entity),
        ];
        code_blocks
    }

    pub fn gen_entity_struct() -> TokenStream {
        quote! {
            #[derive(Copy, Clone, Default, Debug, AkitaTable, ToAkita, FromAkita)]
            pub struct Entity;
        }
    }
}